<?php
/**
 * This file is part of the Agora distribution. 
 * Detailed copyright and licensing information can be found
 * in the gpl-3.0.txt file which should be included in the distribution.
 * 
 * @version		$Id: 3.0.14 Olympus 2010-09-28 rammstein4o $
 * @copyright  2007 - 2010 jVitals
 * @license   GPLv3 Open Source
 * @link       http://jvitals.com
 * @since      File available since initial release
 */	
	defined ('IN_AGORA') or die;

	class AccessModel extends Model
	{
		function __construct()
		{
			parent::__construct('user_group');
			$this->setQuery('SELECT id, name FROM ##__roles');
			$roles = $this->db->loadRowList();
			foreach ($roles as $role) {
				$role_name = strtoupper('AGORA_ROLE_'.$role[1]);
				@define($role_name,intval($role[0]));
				$this->roles[$role[0]] = $role_name;
			}
			if (!defined('AGORA_ROLE_GUEST')) {
				@define('AGORA_ROLE_GUEST',1);
			}
			$this->access = array('read','read_rss','bbcode','bbcode_img','post_reply','post_poll','post_topic','edit_topic','edit_posts','delete_topics','delete_posts', 'make_sticky', 'close_topic', 'use_captcha');
		}

		function setRole($user_id,$group_id, $role_id)
		{
			$this->setQuery('UPDATE ##__user_group SET role_id = '.intval($role_id).' WHERE group_id='.intval($group_id).' AND user_id='.intval($user_id));
			$this->db->query();
		}

		function getRole($user_id,$group_id)
		{
			$this->setQuery('SELECT role_id FROM ##__user_group WHERE group_id = '.intval($group_id).' AND user_id = '.intval($user_id). ' LIMIT 1');
			$role = $this->db->loadResult();
			if (is_null($role)) {
				return AGORA_ROLE_GUEST;
			}

			if (isset($this->roles[$role])) {
				return constant($this->roles[$role]);
			}
		}

		// we call this function like this:
		// $access_model->authenticate($user_id, $forum_id, 'read')
		
		function authenticate($user_id, $forum_id, $action)
		{
			if ($user_id != 0) {
				// arrays of integers
				$this->db->bind('user_id',$user_id,'integer');
				$this->db->bind('forum_id',$forum_id,'integer');

				$this->db->setQuery('SELECT COUNT(*)'.
								' FROM ##__group_forum AS group_forum'.
								' INNER JOIN ##__user_group AS user_group'.
								'  ON user_group.group_id = group_forum.group_id'.
								'  AND user_group.role_id = group_forum.role_id'.
								' INNER JOIN ##__permissions AS permissions'.
								'  ON permissions.id = group_forum.access_id'.
								' WHERE user_group.user_id = :user_id'.
								' AND group_forum.forum_id = :forum_id'.
								' AND permissions.'.$this->db->nameQuote($action).' = 1');
				$allowed = $this->db->loadResult();
			} else {
				//user is guest
				$role_id = AGORA_ROLE_GUEST;
				$this->db->bind('role_id',$role_id,'integer');
				$this->db->bind('forum_id',$forum_id,'integer');

				$this->db->setQuery('SELECT COUNT(*)'.
								' FROM ##__group_forum AS group_forum'.
								' INNER JOIN ##__permissions AS permissions'.
								'  ON permissions.id = group_forum.access_id'.
								' WHERE group_forum.role_id = :role_id'.
								' AND group_forum.forum_id = :forum_id'.
								' AND permissions.'.$this->db->nameQuote($action).' = 1');
				$allowed = $this->db->loadResult();
			}
			return $allowed != '0';
		}

		function authenticateTopic($user_id, $topic_id, $action)
		{
			$this->db->setQuery('SELECT forum_id FROM ##__topics WHERE id='.intval($topic_id));
			$forum_id = $this->db->loadResult();
			if (is_null($forum_id)) return false;
			
			$allowed = $this->authenticate($user_id, $forum_id, $action);
			
			if ($action == 'close_topic' && $allowed == true) {
			
				$user_model = & Model::getInstance('UserModel');
				$agora_user = $user_model->loadCurrent();
				$access = $this->getForumAccess($forum_id, $agora_user['id']);
				$this->db->setQuery('SELECT poster FROM ##__topics WHERE id='.intval($topic_id));
				$topic_poster = $this->db->loadResult();
				if (is_null($topic_poster)) $topic_poster = '';
				
				if ((($agora_user['is_superadmin'] || $access['can_moderate']) || ($agora_user['username'] == $topic_poster)) ) {
					$allowed = true;
				} else {
					$allowed = false;
				}
			}
			
			return $allowed;
		}

		function authenticatePost($user_id, $post_id, $action)
		{
			$this->db->setQuery('SELECT topic_id FROM ##__posts WHERE id='.intval($post_id));
			$topic_id = $this->db->loadResult();
			if (is_null($topic_id)) return false;
			
			return $this->authenticateTopic($user_id, $topic_id, $action);
		}

		function getAllowedForums($user_id, $action= 'read')
		{
			if ($user_id != 0) {
				$this->db->bind('user_id',$user_id,'integer');

				$this->db->setQuery('SELECT forum_id'.
								' FROM ##__group_forum AS group_forum'.
								' INNER JOIN ##__user_group AS user_group'.
								'  ON user_group.group_id = group_forum.group_id'.
								'  AND user_group.role_id = group_forum.role_id'.
								' INNER JOIN ##__permissions AS permissions'.
								'  ON permissions.id = group_forum.access_id'.
								' WHERE user_group.user_id = :user_id'.
								' AND permissions.'.$action.' = 1');
				$allowed_forums = $this->db->loadResultArray();
			} else {
				$role_id = AGORA_ROLE_GUEST;
				$this->db->bind('role_id',$role_id,'integer');
				$this->db->setQuery('SELECT forum_id'.
								' FROM ##__group_forum AS group_forum'.
								' INNER JOIN ##__permissions AS permissions'.
								'  ON permissions.id = group_forum.access_id'.
								' WHERE group_forum.role_id = :role_id'.
								' AND permissions.'.$action.' = 1');
				$allowed_forums = $this->db->loadResultArray();
			}
			return $allowed_forums;
		}

		function filterForums(& $forums, $user_id, $action = 'read')
		{
			$forum_ids = array();

			foreach ($forums as $forum) {
				$forum_ids[] = $forum['id'];
			}

			$allowed_forums = $this->getAllowedForums($user_id, $action);

			// early return
			if (empty($allowed_forums)) {
				$forums = array();
				return;
			}

			foreach ($forums as $key => $forum) {
				if (!in_array($forum['id'], $allowed_forums)) {
					unset($forums[$key]);
				}
			}

		}

		function getForumAccess($forum_id, $user_id)
		{
			$this->db->bind('forum_id',$forum_id,'integer');
			if ($user_id != 0) {
				// real user
				$this->db->bind('user_id',$user_id,'integer');

				$this->db->setQuery('SELECT user_group.role_id, permissions.*'.
								' FROM ##__user_group AS user_group'.
								' INNER JOIN ##__group_forum AS group_forum'.
								'  ON user_group.group_id = group_forum.group_id'.
								'  AND user_group.role_id = group_forum.role_id'.
								' INNER JOIN ##__permissions AS permissions'.
								'  ON permissions.id = group_forum.access_id'.
								' WHERE user_group.user_id = :user_id'.
								' AND group_forum.forum_id = :forum_id');
				$access = $this->db->loadAssocList();
			} else {
				$role_id = AGORA_ROLE_GUEST;
				$this->db->bind('role_id',$role_id,'integer');
				$this->db->setQuery('SELECT group_forum.role_id,permissions.*'.
								' FROM ##__group_forum AS group_forum'.
								' INNER JOIN ##__permissions AS permissions'.
								'  ON permissions.id = group_forum.access_id'.
								' WHERE group_forum.role_id = :role_id'.
								' AND group_forum.forum_id = :forum_id');
				$access = $this->db->loadAssocList();
			}

			$total_access = array();
			
			if (is_array($access) && count($access) > 0) {
				foreach ($access as $access_row) {
					if (($access_row['role_id'] == AGORA_ROLE_MODERATOR || $access_row['role_id'] == AGORA_ROLE_ADMIN) && $access_row['post_reply'] != 0) {
						$access_row['can_moderate'] = 1;
					} else {
						$access_row['can_moderate'] = 0;
					}
					//unset($access_row['role_id']);
					unset($access_row['id']);

					foreach ($access_row as $perm=>$value) {
						
						if (!isset($total_access[$perm])) {
							$total_access[$perm] = intval($value);
							continue;
						}
						$total_access[$perm] = $total_access[$perm] || intval($value);
					}
				}
			}
			return $total_access;
		}

		function getForumGroups($forum_id)
		{
			$this->db->setQuery('SELECT DISTINCT gf.group_id AS id, g.parent_id, g.name'.
						' FROM ##__group AS g'.
						' INNER JOIN ##__group_forum AS gf'.
						'  ON gf.group_id=g.id'.
						' WHERE gf.forum_id='.intval($forum_id));
			return $this->db->loadAssocList();
		}

		function getForumGroupPermissions($forum_id, $group_id)
		{
			$this->db->setQuery('SELECT gf.role_id, perm.*'.
						' FROM ##__permissions AS perm'.
						' INNER JOIN ##__group_forum AS gf'.
						'  ON gf.access_id=perm.id'.
						' WHERE gf.forum_id='.intval($forum_id).
						' AND gf.group_id='.intval($group_id).
						' ORDER BY role_id');
			return $this->db->loadAssocList();
		}

		function setForumGroupPermissions($forum_id, $group_id, $role_id, $permissions)
		{
			$this->db->bind('forum_id',$forum_id,'integer',true);
			$this->db->bind('group_id',$group_id,'integer',true);
			$this->db->bind('role_id',$role_id,'integer',true);

			$this->db->setQuery('SELECT gr_forum.id, perm.id as access_id'.
						' FROM ##__group_forum AS gr_forum'.
						' LEFT JOIN ##__permissions AS perm ON perm.id=access_id'.
						' WHERE group_id=:group_id'.
						  ' AND forum_id=:forum_id'.
						  ' AND role_id =:role_id');
			
			list($id,$access_id) = $this->db->loadRow();

			if (is_null($access_id)) {
				$this->setQuery('INSERT INTO ##__permissions () VALUES ()');
				$this->db->query();
				$access_id = $this->db->insertid();

				if ($id) {
					$this->setQuery('UPDATE ##__group_forum SET'.
								' access_id='.intval($access_id).
								' WHERE id='.intval($id));
					$this->db->query();
				} else {
					$this->setQuery('INSERT INTO ##__group_forum SET'.
								' forum_id=:forum_id,'.
								' group_id=:group_id,'.
								' role_id=:role_id,'.
								' access_id='.intval($access_id));
					$this->db->query();
				}
			}
			$this->db->clear();
			
			$sql = 'UPDATE ##__permissions SET ';

	    	$i = 0;
	    	$total = count($permissions);

			foreach ($permissions as $name=>$value) {
				if ($value) {
					$sql .= $this->db->nameQuote($name).'=1';
				} else {
					$sql .= $this->db->nameQuote($name).'=0';
				}

	    		$i++;
	    		if ($i != $total) {
	    			$sql .= ", ";
	    		}
			}

			$sql .= ' WHERE id='.intval($access_id);

			$this->db->setQuery($sql);
			$this->db->query();
		}

		function getGroupPermissions($group_id)
		{
			$this->db->setQuery('SELECT role_id, access_id FROM ##__group_permissions WHERE group_id = '.intval($group_id));
			$roles = $this->db->loadAssocList();

			$permissions = array();
			foreach ($roles as $role) {
				$this->db->setQuery('SELECT * FROM ##__permissions WHERE id='.intval($role['access_id']));
				$access_line = $this->db->loadAssoc();
				unset($access_line['id']);

/*				foreach ($access_line as $perm_name => $perm_value) {
					if ($perm_value) {
						$permissions[$role['role_id']][] = $perm_name;
					}
				}*/
				$permissions[$role['role_id']] = $access_line;
			}
			return $permissions;
		}

		function setGroupPermissions($group_id,$role_id,$group_perm)
		{
			// fetch access_id from group permissions
			$groupQuery = 'SELECT access_id FROM ##__group_permissions WHERE group_id = '.intval($group_id).' AND role_id ='.intval($role_id);
			$this->db->setQuery($groupQuery);
			$access_id = $this->db->loadResult();
			// fetch old rules before updating so we can check for forums with the same permissions
			$this->db->setQuery('SELECT * FROM ##__permissions WHERE id = '.intval($access_id));
			$old_rules = $this->db->loadAssoc();
			// start building column updates
			$fields = array();
			foreach ($group_perm as $perm_name=>$perm_value) {
				$fields[] = $this->db->nameQuote($perm_name).'='.$this->db->Quote($perm_value);
			}
			// start building WHERE
			$where = array();
			foreach ($old_rules as $perm_name=>$perm_value) {
				if ('id' == $perm_name) continue;
				$where[] = $this->db->nameQuote($perm_name).'='.$this->db->Quote($perm_value);
			}
			// start building sql update statement
			$sql = 'UPDATE ##__permissions SET ';
			// add fields to update
			$sql .= implode(',',$fields);
			// add WHERE
			$sql .= ' WHERE id='.$access_id;
			// update matching forums
			$sql .= ' OR (id IN ((' . str_replace('_permissions', '_forum', $groupQuery) . ')) AND (' . implode(' AND ', $where) . '))';
			// exec query
			$this->db->setQuery($sql);
			$this->db->query();
			
			if (1 < intval($this->db->getAffectedRows())) {
				@JFactory::getApplication()->enqueuemessage(Agora::lang('Please note that changes to group permissions have been propogated to all forums except those whose permissions have been customized.'));
			}
		}

		function getForumGroupCount($forum_id)
		{
			$this->db->setQuery('SELECT COUNT(*) FROM ##__group_forum WHERE forum_id='.intval($forum_id));
			return $this->db->loadResult();
		}

		function addForumGroup($forum_id, $group_id, $access_id = NULL)
		{
			$this->db->setQuery('SELECT COUNT(*) FROM ##__group_forum WHERE group_id='.intval($group_id).' AND forum_id='.intval($forum_id));
			if ($this->db->loadResult() > 0) {
				// already exists
				return;
			}

			//// WE LEAVE THIS TILL STABLE OR 3.1
			// get parent group
			/*
			$this->setQuery('SELECT parent_id FROM ##__group WHERE id='.intval($group_id));
			$parent = $this->db->loadResult();

			$access_fields = '';
			$access_values = '';

			//get permissions from first parent group in this forum
			if ($parent) {
				$access = null;
				$access_id = null;
				// first we assume that our parent is already in this forum

				// find first parent group who has access to this forum
				$this->setQuery('SELECT access.id'.
								' FROM ##__permissions AS access'.
								' INNER JOIN ##__group_forum AS gf'.
								'  ON gf.access_id=access.id'.
								' INNER JOIN ##__group AS grp'.
								'  ON grp.id=gf.group_id'.
								' WHERE grp.id='.intval($parent).
								' AND gf.forum_id='.intval($forum_id));
				$access_id = $this->db->loadResult();

				if ($access_id) {
					$this->setQuery('SELECT access.*'.
								' FROM ##__permissions AS access'.
								' WHERE access.id='.intval($access_id));

					$access = $this->db->loadAssoc();

					unset($access['id']);

					//Quote fields
					$q_fields = array_map(array($this->db,'nameQuote'),array_keys($access));
					//Quote values
					$q_values = array_map(array($this->db,'Quote'),array_values($access));

					$access_fields = implode(',',$q_fields);
					$access_values = implode(',',$q_values);
				}
			}


			$this->setQuery('INSERT INTO ##__permissions ('.$access_fields.') VALUES ('.$access_values.')');
			$this->db->query();
			$access_id = $this->db->insertid();*/

			$access_model = & Model::getInstance('AccessModel');

			$this->db->bind('forum_id',$forum_id,'integer',true);
			$this->db->bind('group_id',$group_id,'integer',true);
			
			$this->db->setQuery('SELECT role_id, permissions.*'.
							' FROM ##__group_permissions AS group_permissions'.
							' INNER JOIN ##__permissions AS permissions'.
							' ON permissions.id = group_permissions.access_id'.
							' WHERE group_id='.intval($group_id));
			$defaults = $this->db->loadAssocList('role_id');

			foreach ($access_model->roles as $role_id=>$role_name) {
				unset($defaults[$role_id]['role_id']);
				unset($defaults[$role_id]['id']);

				$sql = 'INSERT INTO ##__permissions SET ';
				$fields = array();
				foreach ($defaults[$role_id] as $perm_name=>$perm_value) {
					$fields[] = $this->db->nameQuote($perm_name).'='.$this->db->Quote($perm_value);
				}

				$sql .= implode(',',$fields);
				
				$this->db->setQuery($sql);
				$this->db->query();
				$access_id = $this->db->insertid();

				$this->db->bind('role_id',$role_id,'integer');
				$this->db->bind('access_id',$access_id,'integer');

				$this->setQuery('INSERT INTO ##__group_forum (forum_id, group_id, role_id, access_id)'.
							'VALUES (:forum_id, :group_id, :role_id, :access_id)');
				$this->db->query();
			}
			$this->db->clear();
			// no inheritance till 3.1 -> Iskar: inheritance yes!
			$this->setQuery('SELECT parent_id FROM ##__group WHERE id='.intval($group_id));
			$parent = $this->db->loadResult();
			if ((int)$parent != 0) {
				$this->addForumGroup($forum_id, $parent);
			}
		}

		function deleteForumGroup($forum_id, $group_id)
		{
			$this->db->setQuery('SELECT id, access_id FROM ##__group_forum WHERE group_id = '.intval($group_id).' AND forum_id = '.intval($forum_id));
			$roles = $this->db->loadAssocList();

			foreach ($roles as $role) {
				$this->setQuery('DELETE FROM ##__group_forum WHERE id = '.intval($role['id']));
				$this->db->query();

				$this->setQuery('DELETE FROM ##__permissions WHERE id = '.intval($role['access_id']));
				$this->db->query();
			}

		}
	}
?>